use libc::{c_char, c_int, c_long, ftok, key_t, msgget, msgrcv, msgsnd, printf, IPC_CREAT};
use std::ffi::{c_void, CStr, CString};
use std::fmt::{write, Debug, Formatter};
use std::mem;
use std::mem::{transmute, transmute_copy};
use std::ops::Deref;
use std::ptr::{copy, read};

pub fn create_or_get_queue(path: *const c_char, project_id: c_int) -> c_int {
    unsafe {
        let ftok = ftok(path, project_id);
        msgget(ftok, IPC_CREAT | 0666)
    }
}
pub fn queue_send_msg(queue: c_int, msg_type: c_long, msg: Vec<u8>) -> Result<(), String> {
    let msg = if msg.len() > 128 {
        return Err("queue_send_msg cannot exceed 128 bytes".to_string());
    } else {
        let mut vec = [0_u8; 128];
        unsafe { copy(msg.as_ptr(), vec.as_mut_ptr(), msg.len()); }
        vec

    };
    let msg = MSG::new(msg_type, &msg);
    unsafe {
        if msgsnd(queue, msg.as_ptr() as *const c_void, 128, 0) == -1 {
            Err("queue_send_msg error".to_string())
        } else {
            Ok(())
        }
    }
}

pub fn queue_recv_msg(queue: c_int, msg_type: c_long) -> Result<MSG, String> {
    let mut buffer = [0_u8;size_of::<MSG>()];
    unsafe {
        if msgrcv(queue, buffer.as_mut() as * mut _ as *mut c_void, 128, msg_type, 0) == -1 {
            Err("queue_recv_msg error".to_string())
        } else {
            Ok(buffer.into())
        }
    }
}
#[repr(C)]
pub struct MSG {
    my_type: c_long,
    my_text: [c_char; 128],
}
impl MSG {
    pub fn new(my_type: c_long, msg: &[u8;128]) -> Self {
        unsafe {
            Self {
                my_type,
                my_text: transmute_copy(msg),
            }
        }
    }
    pub fn as_ptr(&self) -> *const MSG {
        self as *const MSG
    }
    pub fn as_ptr_mut(&mut self) -> *mut MSG {
        self as *mut MSG
    }
}
impl Debug for MSG {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        unsafe {
            let ptr = CStr::from_ptr(self.my_text.as_ptr());
            let c_string = CString::from(ptr);
            write!(f, "{}", c_string.to_str().unwrap())
        }

    }
}
impl From<[u8; 128+size_of::<c_long>()]> for MSG {
    fn from(value: [u8; 128+size_of::<c_long>()]) -> Self {
        unsafe { transmute(value) }
    }
}